home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Format CD 24
/
Amiga Format AFCD24 (Feb 1998, Issue 108).iso
/
-in_the_mag-
/
emulation
/
amiga
/
uae-0.7.0b2
/
src
/
cpuopti.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-01-20
|
6KB
|
273 lines
/*
* UAE - The Un*x Amiga Emulator
*
* cpuopti.c - Small optimizer for cpu*.s files
* Based on work by Tauno Taipaleenmaki
*
* Copyright 1996 Bernd Schmidt
*/
#include "sysconfig.h"
#include "sysdeps.h"
#include <ctype.h>
#include "options.h"
struct line {
struct line *next, *prev;
int delet;
char *data;
};
struct func {
struct line *first_line, *last_line;
int initial_offset;
};
static void oops(void)
{
fprintf(stderr, "Corrupted assembly file!\n");
abort();
}
static char * match(struct line *l, const char *m)
{
char *str = l->data;
int len = strlen(m);
while (isspace(*str))
str++;
if (strncmp(str, m, len) != 0)
return NULL;
return str + len;
}
static int insn_references_reg (struct line *l, char *reg)
{
if (reg[0] != 'e') {
fprintf(stderr, "Unknown register?!?\n");
abort();
}
if (strstr (l->data, reg) != 0)
return 1;
if (strstr (l->data, reg+1) != 0)
return 1;
if (strcmp (reg, "eax") == 0
&& (strstr (l->data, "%al") != 0 || strstr (l->data, "%ah") != 0))
return 1;
if (strcmp (reg, "ebx") == 0
&& (strstr (l->data, "%bl") != 0 || strstr (l->data, "%bh") != 0))
return 1;
if (strcmp (reg, "ecx") == 0
&& (strstr (l->data, "%cl") != 0 || strstr (l->data, "%ch") != 0))
return 1;
if (strcmp (reg, "edx") == 0
&& (strstr (l->data, "%dl") != 0 || strstr (l->data, "%dh") != 0))
return 1;
return 0;
}
static void do_function(struct func *f)
{
int pops_at_end = 0;
struct line *l, *fl, *l2;
char *s, *s2;
int in_pop_area = 1;
f->initial_offset = 0;
l = f->last_line;
fl = f->first_line;
if (!match(l,"ret"))
oops();
while (!match(fl, "op_"))
fl = fl->next;
fl = fl->next;
/* Try reordering the insns at the end of the function so that the
* pops are all at the end. */
l2 = l->prev;
/* Tolerate one stack adjustment */
if (match (l2, "addl $") && strstr(l2->data, "esp") != 0)
l2 = l2->prev;
for (;;) {
char *forbidden_reg;
struct line *l3, *l4;
while (match (l2, "popl %"))
l2 = l2->prev;
l3 = l2;
for (;;) {
forbidden_reg = match (l3, "popl %");
if (forbidden_reg)
break;
if (l3 == fl)
goto reordered;
/* Jumps and labels put an end to our attempts... */
if (strstr (l3->data, ".L") != 0)
goto reordered;
/* Likewise accesses to the stack pointer... */
if (strstr (l3->data, "esp") != 0)
goto reordered;
/* Function calls... */
if (strstr (l3->data, "call") != 0)
goto reordered;
l3 = l3->prev;
}
if (l3 == l2)
abort();
for (l4 = l2; l4 != l3; l4 = l4->prev) {
/* The register may not be referenced by any of the insns that we
* move the popl past */
if (insn_references_reg (l4, forbidden_reg))
goto reordered;
}
l3->prev->next = l3->next;
l3->next->prev = l3->prev;
l2->next->prev = l3;
l3->next = l2->next;
l2->next = l3;
l3->prev = l2;
}
reordered:
l = l->prev;
s = match(l, "addl $");
s2 = match(fl, "subl $");
if (s && s2) {
if (strcmp(s,s2) == 0) {
int v = 0;
while (isdigit(*s)) {
v = v * 10 + (*s) - '0';
s++;
}
if (strcmp(s, ",%esp") == 0) {
f->initial_offset = v;
l->delet = 2;
fl->delet = 3;
l = l->prev;
fl = fl->next;
} else
in_pop_area = 0;
} else {
fprintf (stderr, "Stack adjustment not matching\n");
in_pop_area = 0;
}
}
while (in_pop_area) {
char *popm, *pushm;
popm = match(l, "popl %");
pushm = match(fl, "pushl %");
if (popm && pushm && strcmp(pushm, popm) == 0) {
pops_at_end++;
fl->delet = l->delet = 1;
} else
in_pop_area = 0;
l = l->prev;
fl = fl->next;
}
if (f->initial_offset)
f->initial_offset += 4 * pops_at_end;
}
static void output_function(struct func *f)
{
struct line *l = f->first_line;
while (l) {
switch (l->delet) {
case 1:
break;
case 0:
printf("%s\n", l->data);
break;
case 2:
if (f->initial_offset)
printf("\taddl $%d,%%esp\n", f->initial_offset);
break;
case 3:
if (f->initial_offset)
printf("\tsubl $%d,%%esp\n", f->initial_offset);
break;
}
l = l->next;
}
}
int main(int argc, char **argv)
{
FILE *infile = stdin;
char tmp[4096];
#ifdef __mc68000__
if(system("perl machdep/cpuopti")==-1) {
perror("perl machdep/cpuopti");
return 10;
} else return 0;
#endif
/* For debugging... */
if (argc == 2)
infile = fopen (argv[1], "r");
for(;;) {
char *s;
if ((fgets(tmp, 4095, infile)) == NULL)
break;
s = strchr (tmp, '\n');
if (s != NULL)
*s = 0;
if (strncmp(tmp, ".globl op_", 10) == 0) {
struct line *first_line = NULL, *prev = NULL;
struct line **nextp = &first_line;
struct func f;
int nr_rets = 0;
int can_opt = 1;
do {
struct line *current;
if (strcmp (tmp, "#APP") != 0 && strcmp (tmp, "#NO_APP") != 0) {
current = *nextp = (struct line *)malloc(sizeof (struct line));
nextp = ¤t->next;
current->prev = prev; prev = current;
current->next = NULL;
current->delet = 0;
current->data = my_strdup (tmp);
if (match (current, "movl %esp,%ebp") || match (current, "enter")) {
fprintf (stderr, "GCC failed to eliminate fp: %s\n", first_line->data);
can_opt = 0;
}
if (match (current, "ret"))
nr_rets++;
}
if ((fgets(tmp, 4095, infile)) == NULL)
oops();
s = strchr (tmp, '\n');
if (s != NULL)
*s = 0;
} while (strncmp (tmp,".Lfe", 4) != 0);
f.first_line = first_line;
f.last_line = prev;
if (nr_rets == 1 && can_opt)
do_function(&f);
/*else
fprintf(stderr, "Too many RET instructions: %s\n", first_line->data);*/
output_function(&f);
}
printf("%s\n", tmp);
}
return 0;
}